package com.tinyideatech.common.utils.http;

import com.alibaba.fastjson.JSONObject;
import com.tinyideatech.common.utils.http.bean.Config;
import org.apache.commons.io.IOUtils;
import org.apache.http.*;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HttpContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import java.io.IOException;
import java.io.InputStream;
import java.io.InterruptedIOException;
import java.io.UnsupportedEncodingException;
import java.net.UnknownHostException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class SingleHttpClient {

    private static final Logger LOGGER = LoggerFactory.getLogger(SingleHttpClient.class);

    // httpClient
    private static CloseableHttpClient httpClient;
    private static PoolingHttpClientConnectionManager manager;
    private static ScheduledExecutorService service;

    private final static Object synLock = new Object();

    // 对线程池中的httpClient做基本配置
    private static final int CONNECT_TIMEOUT = Config.getHttpConnectTimeout();// 设置连接建立的超时时间为10s
    private static final int SOCKET_TIMEOUT = Config.getHttpSocketTimeout();
    private static final int MAX_CONN = Config.getHttpMaxPoolSize(); // 最大连接数
    private static final int Max_PRE_ROUTE = Config.getHttpMaxPoolSize();
    private static final int MAX_ROUTE = Config.getHttpMaxPoolSize();

    private static final SingleHttpClient HTTP_CLIENT = new SingleHttpClient();


    private SingleHttpClient() {

    }

    /**
     * http基本设置
     *
     * @param httpRequestBase
     */
    private static void setRequestConfig(HttpRequestBase httpRequestBase) {
        RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(CONNECT_TIMEOUT)
                .setConnectTimeout(CONNECT_TIMEOUT)
                .setSocketTimeout(SOCKET_TIMEOUT).build();
        httpRequestBase.setConfig(requestConfig);
    }

    /**
     * 获取httpClient实例
     *
     * @param url
     * @return
     */
    public static CloseableHttpClient getHttpClient(String url, int port) {
        String hostname = url.split("/")[2];
        LOGGER.info(hostname);
        if (hostname.contains(":")) {
            String[] args = hostname.split(":");
            hostname = args[0];
            port = Integer.parseInt(args[1]);
        }

        if (httpClient == null) {
            synchronized (synLock) {
                if (httpClient == null) {
                    httpClient = createHttpClient(hostname, port);
                    service = Executors.newScheduledThreadPool(1);
                    service.scheduleAtFixedRate(new TimerTask() {
                        @Override
                        public void run() {
                            manager.closeExpiredConnections();
                            manager.closeIdleConnections(Config.getHttpIdelTimeout(), TimeUnit.MILLISECONDS);
                            LOGGER.info("close expired and idle for over 5s connection");
                        }
                    }, Config.getHttpMonitorInterval(), Config.getHttpMonitorInterval(), TimeUnit.MICROSECONDS);
                }
            }
        }
        return httpClient;
    }

    /**
     * 根据host和port构建httpclient实例
     *
     * @param host
     * @param port
     * @return
     */
    private static CloseableHttpClient createHttpClient(String host, int port) {
        ConnectionSocketFactory plainSocketFactory = PlainConnectionSocketFactory.getSocketFactory();
        LayeredConnectionSocketFactory sslSocketFactory = SSLConnectionSocketFactory.getSocketFactory();
        Registry<ConnectionSocketFactory> registry = RegistryBuilder.<ConnectionSocketFactory>create().register("http", plainSocketFactory)
                .register("https", sslSocketFactory).build();

        manager = new PoolingHttpClientConnectionManager(registry);
        manager.setMaxTotal(MAX_CONN);
        manager.setDefaultMaxPerRoute(Max_PRE_ROUTE);

        HttpHost httpHost = new HttpHost(host, port);
        manager.setMaxPerRoute(new HttpRoute(httpHost), MAX_ROUTE);

        HttpRequestRetryHandler handler = new HttpRequestRetryHandler() {
            @Override
            public boolean retryRequest(IOException e, int i, HttpContext httpContext) {
                if (i > 3) {
                    LOGGER.error("retry has more than 3 time, give up request");
                    return false;
                }
                if (e instanceof NoHttpResponseException) {
                    //服务器没有响应,可能是服务器断开了连接,应该重试
                    LOGGER.error("receive no response from server, retry");
                    return true;
                }
                if (e instanceof SSLHandshakeException) {
                    // SSL握手异常
                    LOGGER.error("SSL hand shake exception");
                    return false;
                }
                if (e instanceof InterruptedIOException) {
                    //超时
                    LOGGER.error("InterruptedIOException");
                    return false;
                }
                if (e instanceof UnknownHostException) {
                    // 服务器不可达
                    LOGGER.error("server host unknown");
                    return false;
                }
                if (e instanceof ConnectTimeoutException) {
                    // 连接超时
                    LOGGER.error("Connection Time out");
                    return false;
                }
                if (e instanceof SSLException) {
                    LOGGER.error("SSLException");
                    return false;
                }

                HttpClientContext clientContext = HttpClientContext.adapt(httpContext);
                HttpRequest httpRequest = clientContext.getRequest();
                return !(httpRequest instanceof HttpEntityEnclosingRequest);

            }
        };
        return HttpClients.custom().setConnectionManager(manager).setRetryHandler(handler).build();
    }

    /**
     * 设置post请求的参数
     *
     * @param httpPost
     * @param params
     */
    private static void setPostParams(HttpPost httpPost, Map<String, String> params) {
        List<NameValuePair> nvps = new ArrayList<NameValuePair>();
        Set<String> keys = params.keySet();
        for (String key : keys) {
            nvps.add(new BasicNameValuePair(key, params.get(key)));
        }
        try {
            httpPost.setEntity(new UrlEncodedFormEntity(nvps, "utf-8"));
            LOGGER.info(String.format("httpPost is [%s]", httpPost));
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }


    public static JSONObject post(String url, int port, Map<String, String> params) {
        HttpPost httpPost = new HttpPost(url);
        setRequestConfig(httpPost);
        setPostParams(httpPost, params);
        CloseableHttpResponse response = null;
        InputStream in = null;
        JSONObject object = new JSONObject();
        try {
            response = getHttpClient(url, port).execute(httpPost, HttpClientContext.create());
            LOGGER.info(String.format("response is [%s]", response));
            HttpEntity entity = response.getEntity();
            if (entity != null) {
                in = entity.getContent();
                int status = response.getStatusLine().getStatusCode();
                String result = IOUtils.toString(in, StandardCharsets.UTF_8);
                LOGGER.info(String.format("result is [%s]", result));
                // object = JSONObject.parseObject(result);
                object.put("description", result);
                object.put("status", status);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (in != null) in.close();
                if (response != null) response.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return object;
    }

    /**
     * 关闭连接池
     */
    public static void closeConnectionPool() {
        try {
            httpClient.close();
            manager.close();
            service.shutdown();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


    public static void main(String[] args) {
        String url = "http://47.104.142.111:8081/auth/register";
        Map<String, String> params = new HashMap<>();
        params.put("mobile", "15010442737");
        params.put("password", "yao123");
        params.put("code", "999999");
        post(url, 8081, params);
        SingleHttpClient.closeConnectionPool();
    }
}

